# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##--------------------------------------------------------------------------##
"""
Dealing Stacks
"""

from .. import pilelib as stack
from .. import helpstrings as hs
from ..gamelib import state
from ..pilelib import move as a_move

class DealGapRowTalon(stack.DealRowTalon):
    """
    Deal per RowStack gap else all.
    """

    @property
    def _piles(self):
        """
        _piles -> tuple

        :returns: `game.s.rows` with gaps or all `game.s.rows`.
        """
        piles = self.game.s['rows']
        return tuple(r for r in piles if not r.cards) or piles

    @property
    def _pilesHaveCards(self):
        """
        _pilesHaveCards -> int

        :returns: whether `game.s.rows` have cards.
        """
        return any(r.cards for r in self.game.s['rows'])


class DealGapRowRedealTalon(stack.DealRowRedealTalon):
    """
    Deal per RowStack gap else all.
    """

    @property
    def _piles(self):
        """
        _piles -> tuple

        :returns: `game.s.rows` with gaps or all `game.s.rows`.
        """
        piles = self.game.s['rows']
        return tuple(r for r in piles if not r.cards) or piles

    @property
    def _pilesHaveCards(self):
        """
        _pilesHaveCards -> int

        :returns: whether `game.s.rows` have cards.
        """
        return any(r.cards for r in self.game.s['rows'])



class Carthage_Talon(stack.DealReserveTalon):
    """
    Deal per ReserveStack except last per RowStack.
    """

    def deal_cards(self, **kw):
        """
        deal_cards([sound=False, shuffle=False]) -> int

        Deal per `game.s.reserves`. When len is len `game.s.rows`, deal per
        `game.s.rows` 1 card.

        :return: num cards dealt.
        """
        if len(self.cards) == len(self.game.s['rows']):
            return self.dealRowAvail(rows=self.game.s['rows'], **kw)
        else:
            return super(Carthage_Talon, self).deal_cards(**kw)


class Primrose_Talon(stack.DealRowRedealTalon):

    @property
    def _piles(self):
        """
        _piles -> tuple

        :returns: `game.s.rows` pile whose index matches `round - 1`.
        """
        return [self.game.s['rows'][self.round - 1]]

    def deal_cards(self, **kw):
        """
        deal_cards([sound=False, shuffle=False]) -> int

        Deal per `game.s.rows`. Redeal pile whose index matches `round - 1`
        per `game.s.rows`, then deal pile remainder.

        :return: num cards dealt.
        """
        ncards = 0
        game = self.game
        if kw.get('sound', False) and game.can_play_sound:
            game._play_sound('deal')
        if not self.cards:
            ncards += self._redeal(rows=self._piles, frames=4)
            if ncards == 0:
                return ncards
            if kw.get('shuffle', False):
                game._move_do(a_move.AShuffleStackMove(self, game.random)
                              )  # low level
            game._move_do(a_move.ANextRoundMove(self), reset=True)  # low level
        for i in range(self.num_deal):
            ncards += self.dealRowAvail(rows=game.s['rows'], **kw)
        while self.cards:
            ncards += self.dealRowAvail(rows=self._piles, **kw)
        return ncards


class Amazons_Talon(stack.DealRowRedealTalon):

    @property
    def _piles(self):
        """
        _pilesHaveCards -> int

        :return: `game.s.rows` with neighbor `game.s.foundations` not gap.
        :rtype: integer
        """
        return [r for f, r in zip(self.game.s['foundations'], self.game.s['rows'])
                if len(f.cards) < f.cap['max_cards']]


class Scuffle_Talon(stack.DealRowRedealTalon):

    def deal_cards(self, **kw):
        """
        deal_cards(sound=False) -> int

        Shuffle before redeal.

        :return: num cards dealt.
        """
        kw.setdefault('shuffle', True)
        return super(Scuffle_Talon, self).deal_cards(self, **kw)


class DealWastesRedealLessTalon(stack.DealRowRedealTalon):

    @property
    def _piles(self):
        """
        _piles -> tup

        :return: deal Game.s.xwastes
        :rtype: tuple
        """
        return self.game.s['xwastes'][:-self.round]


class Cruel_Talon(stack.DealRowRedealTalon):

    def deal_cards(self, rows=None, shuffle=False, sound=False, reverse=False,
                  **kw):
        """
        deal_cards([sound:bool=False, shuffle=False, reverse=False]) -> int

        Actual dealing, called from game.talon_deal(). Overide default, to
        allow cards to be dealt a set to and redealt from _piles.

        :keyword boolean sound: sound fx?
        :keyword boolean shuffle: shuffle cards before redealing?
        :keyword boolean reverse: deals cards in reverse order?
        :return: number of cards dealt.
        """
        ncards, nrows = 0, len(self._piles)
        game = self.game
        if sound and game.can_play_sound:
            game._play_sound('deal')
        if not self.cards:
            ncards += self._redeal(rows=self._piles, frames=4)
            if shuffle:
                game._move_do(a_move.AShuffleStackMove(self, game.random)
                              )  # low level
            if reverse:
                self.cards.reverse()
            game._move_do(a_move.ANextRoundMove(self), reset=True)  # low level
        n = ncards
        deal = [4] * nrows
        extra_cards = ncards - 4 * nrows
        for x in range(extra_cards):
            deal[x % nrows] += 1
        if sound and game.can_play_sound:
            game._play_sound('deal')
        for i in self._piles:
            k = min(deal[i], n)
            frames = (0, 4)[n <= 12]
            for j in range(k):
                self.move_pile(1, self._piles[i], frames=frames)
            n -= k
            if n == 0:
                break
        return ncards


class SeniorWrangler_Talon(stack.DealRowTalon):

    @property
    def _piles(self):
        """
        _piles -> tup

        :return: deal Game.s.rows
        :rtype: tuple
        """
        return [self.game.s['rows'][self.round - 1]]

    @property
    def _other_piles(self):
        """
        _piles -> tup

        :return: deal Game.s.rows
        :rtype: tuple
        """
        return self.game.s['rows'][self.round:]

    def can_deal_cards(self):
        if self.round == self.max_rounds:
            return False
        return not self.game.is_complete

    @state.move_state('deal')
    def deal_cards(self, sound=False):
        ncards = 0
        game = self.game
        if not self._pilesHaveCards:
            game._move_do(a_move.ANextRoundMove(self), reset=True)  # low level
            return 1
        if sound and game.can_play_sound:
            game._play_sound('deal')
        self._redeal(rows=self._piles, frames=4)
        game._move_do(a_move.ANextRoundMove(self), reset=True)  # low level
        ncards += self.dealRowAvail(rows=self.other_piles, sound=False)
        while self.cards:
            ncards += self.dealRowAvail(sound=False)
        return ncards


class SlyFox_Talon(stack.OpenAutoTalon):

    def move_pile(self, ncards, to_stack, **kw):
        """
        Augment to set game status when state S_PLAY and to_stack was s[row] to
        track cards played from pile. Call next method in order.

        .. move_pile(ncards:int, to_stack:Pile [, *animation keywords*]) -> None
        """
        if (self.game.move_state == self.game.S_PLAY and
            to_stack in self.game.s['rows']):
            self.game.status = (self.game.status + 1) % 20
        super(SlyFox_Talon, self).move_pile(ncards, to_stack, **kw)